﻿//*********************************************************************
//
//  文件名：RedisBase
//
//  作者： LW
//
//  作用：Redis缓存
//
//  日期：2015/9/9 15:24:41
//
//*********************************************************************

/*
 * https://github.com/ServiceStack/ServiceStack.Redis
 * redis是一个典型的k/v型数据库
 * redis共支持五种类型的数据 string,list,hash,set,sortedset
 * **/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using ServiceStack.Redis;
using ServiceStack.Text;
using System.Web.Script.Serialization;

namespace Framework.Cache.Redis
{
    /// <summary>
    /// RedisBase
    /// 缓存操作
    /// </summary>
    public class RedisBase
    {
        /// <summary>
        /// 读写连接
        /// </summary>
        private string[] readWriteHosts { get; set; }
        /// <summary>
        /// 只读连接
        /// </summary>
        private string[] readOnlyHosts { get; set; }

        /// <summary>
        /// '读'链接池链接数
        /// </summary>

        private int MaxReadPoolSize { get; set; }
        /// <summary>
        /// '写'链接池链接数
        /// </summary>
        private int MaxWritePoolSize { get; set; }

        private PooledRedisClientManager prcm;// = CreateManager(readWriteHosts, readOnlyHosts);

        /// <summary>
        /// RedisBase
        /// </summary>
        /// <param name="_readWriteHosts">读写连接 password@ip:port</param>
        /// <param name="_readOnlyHosts">读连接 password@ip:port</param>
        public RedisBase(string[] _readWriteHosts, string[] _readOnlyHosts)
        {
            this.readWriteHosts = _readWriteHosts;
            this.readOnlyHosts = _readOnlyHosts;
            this.MaxReadPoolSize = 60;
            this.MaxWritePoolSize = 60;
            this.prcm = this.CreateManager(readWriteHosts, readOnlyHosts);
        }

        /// <summary>
        /// RedisBase
        /// </summary>
        /// <param name="_readWriteHosts">读写连接 password@ip:port</param>
        /// <param name="_readOnlyHosts">读连接 password@ip:port</param>
        /// <param name="_maxWritePoolSize">'写'链接池链接数</param>
        /// <param name="_maxReadPoolSize">'读'链接池链接数 </param>
        public RedisBase(string[] _readWriteHosts, string[] _readOnlyHosts, int _maxWritePoolSize, int _maxReadPoolSize)
        {
            this.readWriteHosts = _readWriteHosts;
            this.readOnlyHosts = _readOnlyHosts;
            this.MaxWritePoolSize = _maxWritePoolSize;
            this.MaxReadPoolSize = _maxReadPoolSize;
            this.prcm = this.CreateManager(readWriteHosts, readOnlyHosts);
        }

        private PooledRedisClientManager CreateManager(string[] _readWriteHosts, string[] _readOnlyHosts)
        {
            if (_readWriteHosts == null || _readOnlyHosts == null || _readWriteHosts.Count() <= 0 || _readOnlyHosts.Count() <= 0)
            {
                throw new Exception("_readWriteHosts OR _readOnlyHosts Can't be empty");
            }

            RedisClientManagerConfig redisConfig = new RedisClientManagerConfig();
            redisConfig.AutoStart = true;
            redisConfig.MaxReadPoolSize = this.MaxReadPoolSize;   //'读'链接池链接数 
            redisConfig.MaxWritePoolSize = this.MaxWritePoolSize; //'写'链接池链接数 
            RedisConfig.VerifyMasterConnections = false;//需要设置

            //支持读写分离，均衡负载  
            PooledRedisClientManager p = new PooledRedisClientManager(_readWriteHosts, _readOnlyHosts, redisConfig);
            return p;
        }

        #region Item 操作

        /// <summary>
        /// 缓存单个值
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="value">value</param>
        /// <returns></returns>
        public bool Item_Set<T>(string key, T value)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                string v = JsonSerializer.SerializeToString<T>(value);
                return redis.Set<string>(key, v);
            }
        }

        /// <summary>
        /// 缓存单个值
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="value">value</param>
        /// <param name="expiresIn">过期时间</param>
        /// <returns></returns>
        public bool Item_Set<T>(string key, T value, TimeSpan expiresIn)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                JavaScriptSerializer serialize = new JavaScriptSerializer();
                string v = serialize.Serialize(value);
                return redis.Set<string>(key, v, expiresIn);
            }
        }

        /// <summary>
        /// 获取单个值
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <returns></returns>
        public T Item_Get<T>(string key) where T : class
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                string item = redis.Get<string>(key);
                var value = JsonSerializer.DeserializeFromString<T>(item);
                return value;
            }
        }

        /// <summary>
        /// 移除单个值
        /// </summary>
        /// <param name="key">key</param>
        /// <returns></returns>
        public bool Item_Remove(string key)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                return redis.Remove(key);
            }
        }

        /// <summary>
        /// 过期
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="expiresIn">过期时间</param>
        /// <returns></returns>
        public bool Item_SetExpire(string key, TimeSpan expiresIn)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                return redis.ExpireEntryIn(key, expiresIn);
            }
        }

        #endregion

        #region List 操作

        /// <summary>
        /// 向集合添加元素
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="value">value</param>
        public void List_Add<T>(string key, T value)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                var redisTypedClient = redis.As<T>();
                redisTypedClient.AddItemToList(redisTypedClient.Lists[key], value);
            }
        }

        /// <summary>
        /// 从集合移除单个元素
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="value">value</param>
        /// <returns></returns>
        public bool List_Remove<T>(string key, T value)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                var redisTypedClient = redis.As<T>();
                return redisTypedClient.RemoveItemFromList(redisTypedClient.Lists[key], value) > 0;
            }
        }

        /// <summary>
        /// 从集合移除指定key的元素
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        public void List_RemoveAll<T>(string key)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                var redisTypedClient = redis.As<T>();
                redisTypedClient.Lists[key].RemoveAll();
            }
        }

        /// <summary>
        /// 获取元素条数
        /// </summary>
        /// <param name="key">key</param>
        /// <returns></returns>
        public long List_Count(string key)
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                return redis.GetListCount(key);
            }
        }

        /// <summary>
        /// 获取分页数据
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="start">start</param>
        /// <param name="count">count</param>
        /// <returns></returns>
        private List<T> List_GetRange<T>(string key, int start, int count)
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                var c = redis.As<T>();
                return c.Lists[key].GetRange(start, start + count - 1);
            }
        }

        /// <summary>
        /// 获取指定key的所有数据
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <returns></returns>
        public List<T> List_GetList<T>(string key)
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                var c = redis.As<T>();
                return c.Lists[key].GetRange(0, c.Lists[key].Count);
            }
        }

        /// <summary>
        /// 获取分页数据
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="pageIndex">pageIndex</param>
        /// <param name="pageSize">pageSize</param>
        /// <returns></returns>
        public List<T> List_GetList<T>(string key, int pageIndex, int pageSize)
        {
            int start = pageSize * (pageIndex - 1);
            return List_GetRange<T>(key, start, pageSize);
        }

        #endregion

        #region Set 操作

        //set是一种集合类型，redis中可以对集合进行交集，并集和互斥运算

        /// <summary>
        /// SetAdd
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Set_Add<T>(string key, T value)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                var redisTypedClient = redis.As<T>();
                redisTypedClient.Sets[key].Add(value);
            }
        }

        /// <summary>
        /// Set判断是否存在
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="value">value</param>
        /// <returns></returns>
        public bool Set_Contains<T>(string key, T value)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                var redisTypedClient = redis.As<T>();
                return redisTypedClient.Sets[key].Contains(value);
            }
        }

        /// <summary>
        /// SetRemove
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="value">value</param>
        /// <returns></returns>
        public bool Set_Remove<T>(string key, T value)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                var redisTypedClient = redis.As<T>();
                return redisTypedClient.Sets[key].Remove(value);
            }
        }

        #endregion

        #region Hash 操作

        // hash类型是一种字典结构，也是最接近RDBMS的数据类型，其存储了字段和字段值的映射，但字段值只能是
        // 字符串类型，散列类型适合存储对象，建议使用对象类别和ID构成键名，使用字段表示对象属性，字
        // 段值存储属性值，例如：car:2 price 500 ,car:2  color black,用redis命令设置散列时，命令格式
        // 如下：HSET key field value，即key，字段名，字段值

        /// <summary>
        /// 判断某个数据是否已经被缓存 
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="dataKey">dataKey</param>
        /// <returns></returns>
        public bool Hash_Exist<T>(string key, string dataKey)
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                return redis.HashContainsEntry(key, dataKey);
            }
        }

        /// <summary>
        /// 存储数据到hash表 
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="dataKey">dataKey</param>
        /// <param name="value">value</param>
        /// <returns></returns>
        public bool Hash_Set<T>(string key, string dataKey, T value)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                string v = JsonSerializer.SerializeToString<T>(value);
                return redis.SetEntryInHash(key, dataKey, v);
            }
        }

        /// <summary>
        /// 移除hash中的某值 
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="dataKey">dataKey</param>
        /// <returns></returns>
        public bool Hash_Remove(string key, string dataKey)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                return redis.RemoveEntryFromHash(key, dataKey);
            }
        }

        /// <summary>
        /// 移除整个hash
        /// </summary>
        /// <param name="key">key</param>
        /// <returns></returns>
        public bool Hash_Remove(string key)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                return redis.Remove(key);
            }
        }

        /// <summary>
        /// 从hash表获取数据 
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="dataKey">dataKey</param>
        /// <returns></returns>
        public T Hash_Get<T>(string key, string dataKey)
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                string value = redis.GetValueFromHash(key, dataKey);
                return JsonSerializer.DeserializeFromString<T>(value);
            }
        }

        /// <summary>
        /// 获取整个hash的数据 
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <returns></returns>
        public List<T> Hash_GetAll<T>(string key)
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                var list = redis.GetHashValues(key);
                if (list != null && list.Count > 0)
                {
                    List<T> result = new List<T>();
                    foreach (var item in list)
                    {
                        var value = JsonSerializer.DeserializeFromString<T>(item);
                        result.Add(value);
                    }
                    return result;
                }
                return null;
            }
        }

        /// <summary>
        /// 设置hash缓存过期 
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="expireIn">expireIn</param>
        public void Hash_SetExpire(string key, TimeSpan expireIn)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                redis.ExpireEntryIn(key, expireIn);
            }
        }

        /// <summary> 
        /// 获取Hash集合数量 
        /// </summary> 
        /// <param name="key">key</param> 
        public long Hash_GetCount(string key)
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                return redis.GetHashCount(key);
            }
        }

        #endregion

        #region SortedSet 操作

        //sorted set是在集合的基础上为每个元素关联了一个“分数”，我们能够
        //获得分数最高的前N个元素，获得指定分数范围内的元素，元素是不同的，但是"分数"可以是相同的
        //set是用散列表和跳跃表实现的，获取数据的速度平均为o(log(N))

        /// <summary>
        /// 添加数据到 SortedSet 
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="value"></param>
        /// <param name="score">score</param>
        /// <returns></returns>
        public bool SortedSet_Add<T>(string key, T value, double score)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                string v = JsonSerializer.SerializeToString<T>(value);
                return redis.AddItemToSortedSet(key, v, score);
            }
        }

        /// <summary>
        /// 移除数据从SortedSet
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">key</param>
        /// <param name="value">value</param>
        /// <returns></returns>
        public bool SortedSet_Remove<T>(string key, T value)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                string v = JsonSerializer.SerializeToString<T>(value);
                return redis.RemoveItemFromSortedSet(key, v);
            }
        }

        /// <summary> 
        /// 修剪SortedSet 
        /// </summary> 
        /// <param name="key"></param> 
        /// <param name="size">保留的条数</param> 
        /// <returns></returns> 
        public long SortedSet_Trim(string key, int size)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                return redis.RemoveRangeFromSortedSet(key, size, 9999999);
            }
        }

        /// <summary> 
        /// 获取SortedSet的长度 
        /// </summary> 
        /// <param name="key">key</param> 
        /// <returns></returns> 
        public long SortedSet_Count(string key)
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                return redis.GetSortedSetCount(key);
            }
        }

        /// <summary> 
        /// 获取SortedSet的分页数据 
        /// </summary> 
        /// <typeparam name="T">T</typeparam> 
        /// <param name="key">key</param> 
        /// <param name="pageIndex">pageIndex</param> 
        /// <param name="pageSize">pageSize</param> 
        /// <returns></returns> 
        public List<T> SortedSet_GetList<T>(string key, int pageIndex, int pageSize)
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                var list = redis.GetRangeFromSortedSet(key, (pageIndex - 1) * pageSize, pageIndex * pageSize - 1);
                if (list != null && list.Count > 0)
                {
                    List<T> result = new List<T>();
                    foreach (var item in list)
                    {
                        var data = JsonSerializer.DeserializeFromString<T>(item);
                        result.Add(data);
                    }
                    return result;
                }
            }
            return null;
        }

        /// <summary> 
        /// 获取SortedSet的全部数据 
        /// </summary> 
        /// <typeparam name="T">T</typeparam> 
        /// <param name="key">key</param> 
        /// <returns></returns> 
        public List<T> SortedSet_GetListALL<T>(string key)
        {
            using (IRedisClient redis = this.prcm.GetReadOnlyClient())
            {
                var list = redis.GetRangeFromSortedSet(key, 0, 9999999);
                if (list != null && list.Count > 0)
                {
                    List<T> result = new List<T>();
                    foreach (var item in list)
                    {
                        var data = JsonSerializer.DeserializeFromString<T>(item);
                        result.Add(data);
                    }
                    return result;
                }
            }
            return null;
        }

        /// <summary> 
        /// 设置SortedSet缓存过期 
        /// </summary> 
        /// <param name="key">key</param> 
        /// <param name="expireIn"></param> 
        public void SortedSet_SetExpire(string key, TimeSpan expireIn)
        {
            using (IRedisClient redis = this.prcm.GetClient())
            {
                redis.ExpireEntryIn(key, expireIn);
            }
        }

        #endregion
    }
}
